package com.lkqm.seckill.service.impl;

import com.lkqm.seckill.dao.SeckillDao;
import com.lkqm.seckill.dao.SuccessKilledDao;
import com.lkqm.seckill.dao.cache.RedisDao;
import com.lkqm.seckill.dictionary.SeckillStateEnum;
import com.lkqm.seckill.dto.Exposer;
import com.lkqm.seckill.dto.SeckillExecution;
import com.lkqm.seckill.entity.Seckill;
import com.lkqm.seckill.entity.SuccessKilled;
import com.lkqm.seckill.exception.RepeatSeckillException;
import com.lkqm.seckill.exception.SeckillClosedException;
import com.lkqm.seckill.exception.SeckillException;
import com.lkqm.seckill.service.SeckillService;
import org.apache.commons.collections.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.util.ObjectUtils;

import javax.inject.Inject;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 秒杀Service实现
 * Created by luokaiqiongmou on 2016/11/27.
 */
@Service
public class SeckillServiceImpl implements SeckillService {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    private final String slat = "fdlskfnvc.xvs;ajdklfjsd;klfjsdl";
    @Inject
    private SeckillDao seckillDao;
    @Inject
    private SuccessKilledDao successKilledDao;
    @Inject
    private RedisDao redisDao;

    public List<Seckill> getSeckillList() {
        return seckillDao.queryAll(0,4);
    }

    public Seckill getById(long seckillId) {
        return seckillDao.queryById(seckillId);
    }

    public Exposer exportSeckillUrl(long seckillId) {
        // 缓存：redis, 使用超时维护一致性
        Seckill seckill = redisDao.getSeckill(seckillId);
        if(seckill == null) {
            seckill = seckillDao.queryById(seckillId);
            if(seckill == null) {
                return new Exposer(false, seckillId);
            } else {
                redisDao.putSeckill(seckill);
            }
        }
        Date startTime = seckill.getStartTime();
        Date endTime = seckill.getEndTime();
        Date nowTime = new Date();
        // 秒杀还未开始或者依据结束
        if(nowTime.getTime() < startTime.getTime()
                || nowTime.getTime() > endTime.getTime()) {
            return new Exposer(false, seckillId, nowTime.getTime(), startTime.getTime(), endTime.getTime());
        }
        // 可以秒杀
        String md5 = getMD5(seckillId);
        return new Exposer(true, md5, seckillId);
    }

    private String getMD5(long seckillId) {
        String base = seckillId + "/" + slat;
        String md5 = DigestUtils.md5DigestAsHex(base.getBytes());
        return md5;
    }

    @Transactional
    public SeckillExecution executeSeckill(long seckillId, long userPhone, String md5) throws SeckillException, RepeatSeckillException, SeckillClosedException {
        if (md5 == null || !md5.equals(getMD5(seckillId))) {
            throw new SeckillException("seckill data rewrite");
        }

        // 执行秒杀逻辑：减库存 + 记录购买行为
        Date nowTime = new Date();
        // 设计数据库操作的代码，需要将可能的异常转化为运行时异常，这样spring可以处理事务
        try {
            // 记录购买行为
            int insertCount = successKilledDao.insertSuccessKilled(seckillId, userPhone);
            if(insertCount <= 0) {
                throw new RepeatSeckillException("repeat seckill");
            } else {
                // 减库存
                int updateCount = seckillDao.reduceSeckill(seckillId, nowTime);
                if (updateCount <= 0) {
                    throw new SeckillClosedException("seckill is closed");
                }
            }
        } catch(SeckillClosedException e ) {
            throw e;
        } catch(RepeatSeckillException e) {
            throw e;
        } catch(Exception e) {
            logger.error("seckill data rewrite: " + e);
            throw new SeckillException("seckill data rewrite", e);
        }
        // 业务逻辑执行成功
        SuccessKilled successKilled = successKilledDao.queryByIdWithSeckill(seckillId, userPhone);
        return new SeckillExecution(SeckillStateEnum.SUCCESS, successKilled);
    }


    public SeckillExecution executeSeckillProcedure(long seckillId, long userPhone, String md5) {
        if (md5 == null || !md5.equals(getMD5(seckillId))) {
            throw new SeckillException("seckill data rewrite");
        }

        Map<String,Object> parameters = new HashMap();
        parameters.put("seckillId", seckillId);
        parameters.put("killPhone", userPhone);
        parameters.put("killTime", new Date());
        parameters.put("result", null); // 获得存储过程返回值
        try {
            seckillDao.killByProcedure(parameters);
            int result = MapUtils.getInteger(parameters,"result", -2);
            if(result == 0) {
                // 业务逻辑执行成功
                SuccessKilled successKilled = successKilledDao.queryByIdWithSeckill(seckillId, userPhone);
                return new SeckillExecution(SeckillStateEnum.SUCCESS, successKilled);
            }
        }catch (Exception e) {
            logger.error(e.getMessage());
        }
        return new SeckillExecution(SeckillStateEnum.DATA_REWRITE);
    }
}
